﻿using System;
using System.Collections.Generic;
using System.Linq;
using Engine;

namespace Game
{
    public static class FCPlantManager
    {
        public static List<TerrainBrush>[] m_treeBrushesByType;

        public static int[] m_treeTrunksByType;

        public static int[] m_treeLeavesByType;

        static FCPlantManager()
        {
            m_treeBrushesByType = new List<TerrainBrush>[EnumUtils.GetEnumValues(typeof(FCTreeType)).Max() + 1];
            m_treeTrunksByType = new int[5] { 939, 940, 941, 943, 942 };
            m_treeLeavesByType = new int[5] { 944, 945, 946, 948, 947 };
            Random random = new Random(33);
            m_treeBrushesByType[0] = new List<TerrainBrush>();
            for (int i = 0; i < 16; i++)
            {
                int[] array = new int[16]
                {
                    5, 6, 7, 7, 8, 8, 9, 9, 9, 10,
                    10, 11, 12, 13, 14, 16
                };
                int height4 = array[i];
                int branchesCount = (int)MathUtils.Lerp(10f, 20f, (float)i / 16f);
                TerrainBrush item = CreateTreeBrush(random, GetTreeTrunkValue(FCTreeType.Coco), GetTreeLeavesValue(FCTreeType.Coco), height4, branchesCount, delegate (int y)
                {
                    float num7 = 0.4f;
                    if ((float)y < 0.2f * (float)height4)
                    {
                        num7 = 0f;
                    }
                    else if ((float)y >= 0.2f * (float)height4 && y <= height4)
                    {
                        num7 *= 1.5f;
                    }

                    return num7;
                }, delegate (int y)
                {
                    if ((float)y < (float)height4 * 0.3f || (float)y > (float)height4 * 0.9f)
                    {
                        return 0f;
                    }

                    float num6 = (((float)y < (float)height4 * 0.7f) ? (0.5f * (float)height4) : (0.35f * (float)height4));
                    return random.Float(0.33f, 1f) * num6;
                });
                m_treeBrushesByType[0].Add(item);
            }

            m_treeBrushesByType[1] = new List<TerrainBrush>();
            for (int j = 0; j < 16; j++)
            {
                int[] array2 = new int[16]
                {
                    4, 5, 6, 6, 7, 7, 7, 8, 8, 8,
                    9, 9, 9, 10, 10, 11
                };
                int height3 = array2[j];
                int branchesCount2 = (int)MathUtils.Lerp(0f, 20f, (float)j / 16f);
                TerrainBrush item2 = CreateTreeBrush(random, GetTreeTrunkValue(FCTreeType.Orange), GetTreeLeavesValue(FCTreeType.Orange), height3, branchesCount2, delegate (int y)
                {
                    float num5 = 0.66f;
                    if (y < height3 / 2 - 1)
                    {
                        num5 = 0f;
                    }
                    else if (y > height3 / 2 && y <= height3)
                    {
                        num5 *= 1.5f;
                    }

                    return num5;
                }, (int y) => ((float)y >= (float)height3 * 0.35f && (float)y <= (float)height3 * 0.75f) ? random.Float(0f, 0.33f * (float)height3) : 0f);
                m_treeBrushesByType[1].Add(item2);
            }

            m_treeBrushesByType[2] = new List<TerrainBrush>();
            for (int k = 0; k < 16; k++)
            {
                int[] array3 = new int[16]
                {
                    7, 8, 9, 10, 10, 11, 11, 12, 12, 13,
                    13, 14, 14, 15, 16, 17
                };
                int height2 = array3[k];
                int branchesCount3 = height2 * 3;
                TerrainBrush item3 = CreateTreeBrush(random, GetTreeTrunkValue(FCTreeType.Apple), GetTreeLeavesValue(FCTreeType.Apple), height2, branchesCount3, delegate (int y)
                {
                    float num4 = MathUtils.Lerp(1.4f, 0.3f, (float)y / (float)height2);
                    if (y < 3)
                    {
                        num4 = 0f;
                    }

                    if (y % 2 == 0)
                    {
                        num4 *= 0.3f;
                    }

                    return num4;
                }, delegate (int y)
                {
                    if (y < 3 || (float)y > (float)height2 * 0.8f)
                    {
                        return 0f;
                    }

                    return (y % 2 != 0) ? MathUtils.Lerp(0.3f * (float)height2, 0f, MathUtils.Saturate((float)y / (float)height2)) : 0f;
                });
                m_treeBrushesByType[2].Add(item3);
            }

            m_treeBrushesByType[3] = new List<TerrainBrush>();
            for (int l = 0; l < 16; l++)
            {
                m_treeBrushesByType[3].Add(CreateCherryBlossomTreeBrush(random, MathUtils.Lerp(6f, 9f, (float)l / 15f)));
            }

            m_treeBrushesByType[4] = new List<TerrainBrush>();
            for (int m = 0; m < 16; m++)
            {
                int[] array4 = new int[18]
                {
                    30, 31, 22, 33, 34, 34, 35, 35, 36, 36,
                    47, 47, 58, 58, 59, 59, 60, 60
                };
                int height = array4[m];
                int branchesCount4 = height * 3;
                float startHeight = (0.3f + (float)(m % 4) * 0.05f) * (float)height;
                TerrainBrush item4 = CreateLorejunTreeBrush(random, GetTreeTrunkValue(FCTreeType.Lorejun), GetTreeLeavesValue(FCTreeType.Lorejun), height, branchesCount4, delegate (int y)
                {
                    float num2 = MathUtils.Saturate((float)y / (float)height);
                    float num3 = MathUtils.Lerp(1.5f, 0f, MathUtils.Saturate((num2 - 0.6f) / 0.4f));
                    if ((float)y < startHeight)
                    {
                        num3 = 0f;
                    }

                    if (y % 3 != 0 && y < height - 4)
                    {
                        num3 *= 0.2f;
                    }

                    return num3;
                }, delegate (int y)
                {
                    float num = MathUtils.Saturate((float)y / (float)height);
                    if (y % 3 != 0)
                    {
                        return 0f;
                    }

                    if ((float)y >= startHeight)
                    {
                        return MathUtils.Lerp(0.18f * (float)height, 0f, MathUtils.Saturate((num - 0.6f) / 0.4f));
                    }

                    return ((float)y < startHeight - 4f) ? 0f : (0.1f * (float)height);
                });
                m_treeBrushesByType[4].Add(item4);
            }
        }

        public static int GetTreeTrunkValue(FCTreeType treeType)
        {
            return m_treeTrunksByType[(int)treeType];
        }

        public static int GetTreeLeavesValue(FCTreeType treeType)
        {
            return m_treeLeavesByType[(int)treeType];
        }

        public static ReadOnlyList<TerrainBrush> GetTreeBrushes(FCTreeType treeType)
        {
            return new ReadOnlyList<TerrainBrush>(m_treeBrushesByType[(int)treeType]);
        }

        public static int GenerateRandomPlantValue(Random random, int groundValue, int temperature, int humidity, int y)
        {
            switch (Terrain.ExtractContents(groundValue))
            {
                default:
                    return 0;
                case 7:
                    if (humidity >= 8 || random.Float(0f, 1f) >= 0.01f)
                    {
                        return 0;
                    }

                    if (random.Float(0f, 1f) < 0.05f)
                    {
                        return Terrain.MakeBlockValue(99, 0, 0);
                    }

                    return Terrain.MakeBlockValue(28, 0, 0);
                case 2:
                case 8:
                    if (humidity >= 6)
                    {
                        if (random.Float(0f, 1f) < (float)humidity / 60f)
                        {
                            int result = Terrain.MakeBlockValue(19, 0, TallGrassBlock.SetIsSmall(0, isSmall: false));
                            if (!SubsystemWeather.IsPlaceFrozen(temperature, y))
                            {
                                float num = random.Float(0f, 1f);
                                if (num < 0.04f)
                                {
                                    result = Terrain.MakeBlockValue(20);
                                }
                                else if (num < 0.07f)
                                {
                                    result = Terrain.MakeBlockValue(24);
                                }
                                else if (num < 0.075f)
                                {
                                    result = Terrain.MakeBlockValue(937);
                                }
                                else if (num < 0.09f)
                                {
                                    result = Terrain.MakeBlockValue(25);
                                }
                                else if (num < 0.17f)
                                {
                                    result = Terrain.MakeBlockValue(174, 0, RyeBlock.SetIsWild(RyeBlock.SetSize(0, 7), isWild: true));
                                }
                                else if (num < 0.19f)
                                {
                                    result = Terrain.MakeBlockValue(204, 0, CottonBlock.SetIsWild(CottonBlock.SetSize(0, 2), isWild: true));
                                }
                                else if (num < 0.3f)
                                {
                                    result = Terrain.MakeBlockValue(924, 0, FCShuidaoBlock.SetIsWild(RyeBlock.SetSize(0, 7), isWild: true));
                                }
                            }

                            return result;
                        }
                    }
                    else if (random.Float(0f, 1f) < 0.025f)
                    {
                        if (random.Float(0f, 1f) < 0.2f)
                        {
                            return Terrain.MakeBlockValue(99, 0, 0);
                        }

                        return Terrain.MakeBlockValue(28, 0, 0);
                    }

                    return 0;
            }
        }

        public static FCTreeType? GenerateRandomTreeType(Random random, int temperature, int humidity, int y, float densityMultiplier = 1f)
        {
            FCTreeType? result = null;
            float num = random.Float() * CalculateTreeProbability(FCTreeType.Coco, temperature, humidity, y);
            float num2 = random.Float() * CalculateTreeProbability(FCTreeType.Orange, temperature, humidity, y);
            float num3 = random.Float() * CalculateTreeProbability(FCTreeType.Apple, temperature, humidity, y);
            float num4 = random.Float() * CalculateTreeProbability(FCTreeType.Yinghua, temperature, humidity, y);
            float num5 = random.Float() * CalculateTreeProbability(FCTreeType.Lorejun, temperature, humidity, y);
            float num6 = MathUtils.Max(MathUtils.Max(num, num2, num3, num4), num5);
            if (num6 > 0f)
            {
                if (num6 == num)
                {
                    result = FCTreeType.Coco;
                }

                if (num6 == num2)
                {
                    result = FCTreeType.Orange;
                }

                if (num6 == num3)
                {
                    result = FCTreeType.Apple;
                }

                if (num6 == num4)
                {
                    result = FCTreeType.Yinghua;
                }

                if (num6 == num5)
                {
                    result = FCTreeType.Lorejun;
                }
            }

            if (result.HasValue && random.Bool(densityMultiplier * CalculateTreeDensity(result.Value, temperature, humidity, y)))
            {
                return result;
            }

            return null;
        }

        public static float CalculateTreeDensity(FCTreeType treeType, int temperature, int humidity, int y)
        {
            switch (treeType)
            {
                case FCTreeType.Coco:
                case FCTreeType.Orange:
                case FCTreeType.Apple:
                case FCTreeType.Yinghua:
                    return RangeProbability(humidity, 4f, 15f, 15f, 15f);
                case FCTreeType.Lorejun:
                    return 0.03f;
                default:
                    return 0f;
            }
        }


        public static float CalculateTreeProbability(FCTreeType treeType, int temperature, int humidity, int y)
        {
            float probability = 0f;
            switch (treeType)
            {
                case FCTreeType.Coco:
                case FCTreeType.Orange:
                case FCTreeType.Apple:
                case FCTreeType.Lorejun:
                case FCTreeType.Yinghua:
                    probability = RangeProbability(temperature, 4f, 10f, 15f, 15f) * RangeProbability(humidity, 6f, 8f, 15f, 15f);
                    break;
                default:
                    probability = 0f;
                    break;
            }
            return probability;
        }


        public static float RangeProbability(float v, float a, float b, float c, float d)
        {
            if (v < a)
            {
                return 0f;
            }

            if (v < b)
            {
                return (v - a) / (b - a);
            }

            if (v <= c)
            {
                return 1f;
            }

            if (v <= d)
            {
                return 1f - (v - c) / (d - c);
            }

            return 0f;
        }

        public static TerrainBrush CreateTreeBrush(Random random, int woodIndex, int leavesIndex, int height, int branchesCount, Func<int, float> leavesProbabilityByHeight, Func<int, float> branchesLengthByHeight)
        {
            TerrainBrush terrainBrush = new TerrainBrush();
            terrainBrush.AddRay(0, -1, 0, 0, height, 0, 1, 1, 1, woodIndex);
            for (int i = 0; i < branchesCount; i++)
            {
                int x = 0;
                int num = random.Int(0, height);
                int z = 0;
                float num2 = branchesLengthByHeight(num);
                Vector3 vector = Vector3.Normalize(new Vector3(random.Float(-1f, 1f), random.Float(0f, 0.33f), random.Float(-1f, 1f))) * num2;
                int x2 = (int)MathUtils.Round(vector.X);
                int y = num + (int)MathUtils.Round(vector.Y);
                int z2 = (int)MathUtils.Round(vector.Z);
                int cutFace = 0;
                if (MathUtils.Abs(vector.X) == MathUtils.Max(MathUtils.Abs(vector.X), MathUtils.Abs(vector.Y), MathUtils.Abs(vector.Z)))
                {
                    cutFace = 1;
                }
                else if (MathUtils.Abs(vector.Y) == MathUtils.Max(MathUtils.Abs(vector.X), MathUtils.Abs(vector.Y), MathUtils.Abs(vector.Z)))
                {
                    cutFace = 4;
                }

                terrainBrush.AddRay(x, num, z, x2, y, z2, 1, 1, 1, (Func<int?, int?>)((int? v) => v.HasValue ? null : new int?(Terrain.MakeBlockValue(woodIndex, 0, WoodBlock.SetCutFace(0, cutFace)))));
            }

            for (int j = 0; j < 3; j++)
            {
                terrainBrush.CalculateBounds(out var min, out var max);
                for (int k = min.X - 1; k <= max.X + 1; k++)
                {
                    for (int l = min.Z - 1; l <= max.Z + 1; l++)
                    {
                        for (int m = 1; m <= max.Y + 1; m++)
                        {
                            float num3 = leavesProbabilityByHeight(m);
                            if (random.Float(0f, 1f) < num3 && !terrainBrush.GetValue(k, m, l).HasValue && (terrainBrush.CountNonDiagonalNeighbors(k, m, l, leavesIndex) != 0 || terrainBrush.CountNonDiagonalNeighbors(k, m, l, (Func<int?, int>)((int? v) => (v.HasValue && Terrain.ExtractContents(v.Value) == woodIndex) ? 1 : 0)) != 0))
                            {
                                terrainBrush.AddCell(k, m, l, 0);
                            }
                        }
                    }
                }

                terrainBrush.Replace(0, leavesIndex);
            }

            terrainBrush.AddCell(0, height, 0, leavesIndex);
            terrainBrush.Compile();
            return terrainBrush;
        }

        public static TerrainBrush CreateCherryBlossomTreeBrush(Random random, float size)
        {
            int num = 8;
            TerrainBrush terrainBrush = new TerrainBrush();
            int num2 = m_treeTrunksByType[3];
            int num3 = m_treeLeavesByType[3];
            terrainBrush.AddRay(0, -1, 0, 0, num, 0, 1, 1, 1, num2);
            List<Point3> list = new List<Point3>();
            float num4 = random.Float(0f, (float)Math.PI * 2f);
            for (int i = 0; i < 6; i++)
            {
                float radians = num4 + (float)i * MathUtils.DegToRad(120f);
                Vector3 vector = Vector3.Transform(Vector3.Normalize(new Vector3(1f, random.Float(1f, 1.5f), 0f)), Matrix.CreateRotationY(radians));
                int num5 = random.Int((int)(0.7 * (double)size), (int)(1.2 * (double)size));
                Point3 p = new Point3(0, num - 1, 0);
                Point3 item = new Point3(Vector3.Round(new Vector3(p) + vector * num5));
                terrainBrush.AddRay(p.X, p.Y, p.Z, item.X, item.Y, item.Z, 1, 1, 1, num2);
                list.Add(item);
            }

            foreach (Point3 item2 in list)
            {
                float num6 = random.Float(0.45f * size, 0.6f * size);
                int num7 = (int)MathUtils.Ceiling(num6);
                int num8 = num;
                int num9 = item2.Y + num8 + num7;
                for (int j = item2.X - num7; j <= item2.X + num7; j++)
                {
                    for (int k = item2.Y; k <= num9; k++)
                    {
                        for (int l = item2.Z - num7; l <= item2.Z + num7; l++)
                        {
                            int num10 = MathUtils.Abs(j - item2.X) + MathUtils.Abs(l - item2.Z);
                            float num11 = ((new Vector3(j, k, l) - new Vector3(item2)) * new Vector3(1f, 1.7f, 1f)).Length();
                            if (num11 <= num6 && num10 <= num7 && !terrainBrush.GetValue(j, k, l).HasValue)
                            {
                                terrainBrush.AddCell(j, k, l, num3);
                            }
                        }
                    }
                }
            }

            terrainBrush.Compile();
            return terrainBrush;
        }

        public static TerrainBrush CreateLorejunTreeBrush(Random random, int woodIndex, int leavesIndex, int height, int branchesCount, Func<int, float> leavesProbabilityByHeight, Func<int, float> branchesLengthByHeight)
        {
            int num = random.Int(1, 17);
            float num2 = MathUtils.Lerp(6f, 9f, (float)num / 15f);
            TerrainBrush terrainBrush = new TerrainBrush();
            terrainBrush.AddRay(0, -1, 0, 0, height, 0, 3, 3, 3, woodIndex);
            for (int i = 0; i < branchesCount; i++)
            {
                int x = 0;
                int num3 = random.Int(0, height);
                int z = 0;
                float num4 = branchesLengthByHeight(num3);
                Vector3 vector = Vector3.Normalize(new Vector3(random.Float(-1f, 1f), random.Float(0f, 0.33f), random.Float(-1f, 1f))) * num4;
                int x2 = (int)MathUtils.Round(vector.X);
                int y = num3 + (int)MathUtils.Round(vector.Y);
                int z2 = (int)MathUtils.Round(vector.Z);
                int cutFace = 0;
                if (MathUtils.Abs(vector.X) == MathUtils.Max(MathUtils.Abs(vector.X), MathUtils.Abs(vector.Y), MathUtils.Abs(vector.Z)))
                {
                    cutFace = 1;
                }
                else if (MathUtils.Abs(vector.Y) == MathUtils.Max(MathUtils.Abs(vector.X), MathUtils.Abs(vector.Y), MathUtils.Abs(vector.Z)))
                {
                    cutFace = 4;
                }

                terrainBrush.AddRay(x, num3, z, x2, y, z2, 1, 1, 1, (Func<int?, int?>)((int? v) => v.HasValue ? null : new int?(Terrain.MakeBlockValue(woodIndex, 0, WoodBlock.SetCutFace(0, cutFace)))));
            }

            for (int j = 0; j < 3; j++)
            {
                terrainBrush.CalculateBounds(out var min, out var max);
                for (int k = min.X - 1; k <= max.X + 1; k++)
                {
                    for (int l = min.Z - 1; l <= max.Z + 1; l++)
                    {
                        for (int m = 1; m <= max.Y + 1; m++)
                        {
                            float num5 = leavesProbabilityByHeight(m);
                            if (random.Float(0f, 1f) < num5 && !terrainBrush.GetValue(k, m, l).HasValue && (terrainBrush.CountNonDiagonalNeighbors(k, m, l, leavesIndex) != 0 || terrainBrush.CountNonDiagonalNeighbors(k, m, l, (Func<int?, int>)((int? v) => (v.HasValue && Terrain.ExtractContents(v.Value) == woodIndex) ? 1 : 0)) != 0))
                            {
                                terrainBrush.AddCell(k, m, l, 0);
                            }
                        }
                    }
                }

                terrainBrush.Replace(0, leavesIndex);
            }

            terrainBrush.AddCell(0, height, 0, leavesIndex);
            List<Point3> list = new List<Point3>();
            float num6 = random.Float(0f, (float)Math.PI * 2f);
            for (int n = 0; n < 6; n++)
            {
                float radians = num6 + (float)n * MathUtils.DegToRad(120f);
                Vector3 vector2 = Vector3.Transform(Vector3.Normalize(new Vector3(1f, random.Float(1f, 1.5f), 0f)), Matrix.CreateRotationY(radians));
                int num7 = random.Int((int)(0.7 * (double)num2), (int)(1.2 * (double)num2));
                Point3 p = new Point3(0, height - 1, 0);
                Point3 item = new Point3(Vector3.Round(new Vector3(p) + vector2 * num7));
                terrainBrush.AddRay(p.X, p.Y, p.Z, item.X, item.Y, item.Z, 1, 1, 1, woodIndex);
                list.Add(item);
            }

            foreach (Point3 item2 in list)
            {
                float num8 = random.Float(0.45f * num2, 0.6f * num2);
                int num9 = (int)MathUtils.Ceiling(num8);
                int num10 = item2.Y + height + num9;
                for (int num11 = item2.X - num9; num11 <= item2.X + num9; num11++)
                {
                    for (int num12 = item2.Y; num12 <= num10; num12++)
                    {
                        for (int num13 = item2.Z - num9; num13 <= item2.Z + num9; num13++)
                        {
                            int num14 = MathUtils.Abs(num11 - item2.X) + MathUtils.Abs(num13 - item2.Z);
                            float num15 = ((new Vector3(num11, num12, num13) - new Vector3(item2)) * new Vector3(1f, 1.7f, 1f)).Length();
                            if (num15 <= num8 && num14 <= num9 && !terrainBrush.GetValue(num11, num12, num13).HasValue)
                            {
                                terrainBrush.AddCell(num11, num12, num13, leavesIndex);
                            }
                        }
                    }
                }
            }

            terrainBrush.Compile();
            return terrainBrush;
        }
    }
}